FreeType2 渲染字体的示例
FreeType2有水平、竖直两种文字布局,注意原点的位置。
参考: https://freetype.org/freetype2/docs/glyphs/glyphs-3.html
主要使用到两个 API 函数:
1// 加载字符
2FT_Error FT_Load_Glyph(FT_Face face, FT_UInt glyph_index, FT_Int32 load_flags);
3
4// 渲染 Bitmap
5FT_Error FT_Render_Glyph(FT_GlyphSlot slot, FT_Render_Mode render_mode);通常将 render_mode 参数设为 FT_RENDER_MODE_NORMAL,这样渲染出来的 Bitmap 是抗锯齿的 8 位灰度图。
如果使用 SDL2 之类的图形库将字符绘制到界面上时,需要使用 RGB 格式的像素数据。
则可以将render_mode 参数设为 FT_RENDER_MODE_LCD。
此时,需要将 load_flags 参数设为 FT_LOAD_TARGET_LCD。
这样渲染出来的 Bitmap 仍是 8 位灰度图,但是有三个通道(即R、G、B相同),并且宽度值为实际宽度的 3 倍。
参考:
示例
1#include <stdio.h>
2#include <stdlib.h>
3
4#include <ft2build.h>
5#include FT_FREETYPE_H
6
7#include <SDL2/SDL.h>
8
9#define WINDOW_WIDTH 640
10#define WINDOW_HEIGHT 480
11
12void draw_slot(SDL_Renderer* renderer, int x, int y, FT_GlyphSlot slot)
13{
14 SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGB24, SDL_TEXTUREACCESS_TARGET, slot->bitmap.width/3, slot->bitmap.rows);
15 SDL_UpdateTexture(texture, NULL, slot->bitmap.buffer, slot->bitmap.pitch);
16
17 SDL_Rect rect;
18 rect.x = x + slot->bitmap_left;
19 rect.y = y - slot->bitmap_top;
20 rect.w = slot->bitmap.width/3;
21 rect.h = slot->bitmap.rows;
22 SDL_RenderCopy(renderer, texture, NULL, &rect);
23 SDL_DestroyTexture(texture);
24}
25
26void draw_text(SDL_Renderer* renderer, FT_Face face, int x, int y, const char* text)
27{
28 // 为了方便并直观的绘制文字,这个函数的参数 x、y 为图块的坐上点坐标。
29 // 而为了将字符对齐,freetype2 的原点在基线上,因此需要进行坐标换算。
30 // ascender 是原点到行顶部的高度(正值)
31 // descender 是字符到行底部的高度(负值)
32 y += face->size->metrics.ascender / 64;
33
34 for (const char* ch = text; *ch != 0; ch++)
35 {
36 // 加载文字
37 FT_UInt index = FT_Get_Char_Index(face, (FT_ULong)*ch);
38 FT_Load_Glyph(face, index, FT_LOAD_TARGET_LCD);
39
40 // 渲染
41 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_LCD);
42
43 // 显示
44 draw_slot(renderer, x, y, face->glyph);
45
46 // 步进
47 x += face->glyph->advance.x/64;
48 y += face->glyph->advance.y/64;
49 }
50}
51
52int main(int argc, char* argv[])
53{
54 if (argc != 3)
55 {
56 printf("Usage: %s <font-file> <text>\n", argv[0]);
57 printf(" %s FreeMono.ttf \"Hello World!\"\n", argv[0]);
58 return EXIT_FAILURE;
59 }
60
61 SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
62
63 SDL_Window* window = SDL_CreateWindow("freetype2 demo",
64 SDL_WINDOWPOS_UNDEFINED,
65 SDL_WINDOWPOS_UNDEFINED,
66 WINDOW_WIDTH,
67 WINDOW_HEIGHT,
68 SDL_WINDOW_SHOWN);
69
70 SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
71
72 // 初始化 FreeType 库
73 FT_Library library;
74 FT_Error err = FT_Init_FreeType(&library);
75 if (err != FT_Err_Ok)
76 {
77 fprintf(stderr, "%s\n", FT_Error_String(err));
78 return EXIT_FAILURE;
79 }
80
81 // 加载字体
82 FT_Face face;
83 err = FT_New_Face(library, argv[1], 0, &face);
84 if (err != FT_Err_Ok)
85 {
86 fprintf(stderr, "%s\n", FT_Error_String(err));
87 return EXIT_FAILURE;
88 }
89
90 // 设置尺寸
91 err = FT_Set_Pixel_Sizes(face, 64, 64);
92 if (err != FT_Err_Ok)
93 {
94 fprintf(stderr, "%s\n", FT_Error_String(err));
95 return EXIT_FAILURE;
96 }
97
98 while (1)
99 {
100 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
101 SDL_RenderClear(renderer);
102
103 SDL_Event ev;
104 while(SDL_PollEvent(&ev) > 0)
105 {
106 if (ev.type == SDL_QUIT)
107 goto EXIT;
108 }
109
110 draw_text(renderer, face, 0, 0, argv[2]);
111 SDL_RenderPresent(renderer);
112 }
113
114EXIT:
115 FT_Done_Face(face);
116 FT_Done_FreeType(library);
117 SDL_DestroyRenderer(renderer);
118 SDL_DestroyWindow(window);
119 SDL_Quit();
120
121 return EXIT_SUCCESS;
122}